#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <string>
#include <sstream>

#include <list>
#include <pthread.h>

typedef std::list<int> connection_l;

const int NUM_OF_THREADS = 5;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
connection_l connections;

void * woker_thread(void *)
{
	std::string log_file("./thread_log");
	std::stringstream out;
	out << (uint)pthread_self();
	log_file += out.str();
	FILE * thread_log = fopen(log_file.c_str(), "w");

	printf("New thread created!\nPID of its process: %d. The ID of this thread is: %u\nLog file name is: %s\n\n",
			getpid(), (unsigned int)pthread_self(), log_file.c_str());

	while(true)
	{
		if(connections.size() == 0)
		{
			sleep(1);
			continue;
		}

		int sockfd;
		{
			pthread_mutex_lock( &mutex );
			sockfd = connections.back();
			connections.pop_back();
			printf("Thread with ID %u get connection!\n", (unsigned int)pthread_self());

			timeval tv;
			gettimeofday(&tv, NULL);

			fprintf(thread_log, "Start time of thread %u: %li,%li\n", (unsigned int)pthread_self(), tv.tv_sec - 1300700000, tv.tv_usec);
			fflush(thread_log);

			pthread_mutex_unlock( &mutex );
		}


		int t;
		for(int i = 0; i < 25000; i++)
			for(int j = 0; j < 25000; j++)
				t = i + j;

		int data_size = 1024;
		char buf[data_size];
		bzero(buf, data_size);

		strcpy(buf, "Get off!");
		send(sockfd, buf, data_size, 0);

		close(sockfd);
		printf("Thread with ID %u close connection\n", (unsigned int)pthread_self());


		timeval tv;
		gettimeofday(&tv, NULL);

		fprintf(thread_log, "Finish time of thread %u: %li,%li\n", (unsigned int)pthread_self(), tv.tv_sec - 1300700000, tv.tv_usec);
		fflush(thread_log);
	}

	return NULL;
}

void error(const char *msg)
{
    perror(msg);
    exit(1);
}

int main(int argc, char *argv[])
{
	int data_size = 1024*10000;
	char buf[data_size];
	int numbytes;

     int sockfd, newsockfd, portno;
     socklen_t clilen;
     struct sockaddr_in serv_addr, cli_addr;

     if (argc < 2) {
         fprintf(stderr,"ERROR, no port provided\n");
         exit(1);
     }

     sockfd = socket(PF_INET, SOCK_STREAM, 0);
     if (sockfd < 0)
        error("ERROR opening socket");

     bzero((char *) &serv_addr, sizeof(serv_addr));
     portno = atoi(argv[1]);
     serv_addr.sin_family = PF_INET;
     serv_addr.sin_addr.s_addr = INADDR_ANY;
     serv_addr.sin_port = htons(portno);
     if (bind(sockfd, (struct sockaddr *) &serv_addr,
    		 sizeof(serv_addr)) < 0)
    	 error("ERROR on binding");

     listen(sockfd, 10);

     clilen = sizeof(cli_addr);

     pthread_t work_threads[NUM_OF_THREADS];

     printf("Start threads creation:\n\n");
     for(int i = 0; i < NUM_OF_THREADS; i++)
     {
    	 pthread_create(&work_threads[i], NULL, woker_thread, NULL);
     }

     FILE * server_log = fopen("./server_log", "w");

     while(true)
     {
    	 newsockfd = accept(sockfd,
    			 (struct sockaddr *) &cli_addr,
    			 &clilen);

    	 timeval tv;
    	 gettimeofday(&tv, NULL);
    	 fprintf(server_log, "Get connection at %li,%li\n", tv.tv_sec - 1300700000, tv.tv_usec);
    	 fflush(server_log);

    	 if (newsockfd < 0)
    		 error("ERROR on accept");

    	 bzero(buf, data_size);

    	 if( (numbytes = recv(newsockfd, buf, data_size, 0)) < -1 )
    	 {
    		 close(newsockfd);
    		 error("Can't receive\n");
    	 }

    	 printf("Received connection with length %i\n", numbytes);
    	 buf[numbytes-1] = '\0';
    	 /*
    	 if(strcmp(buf, "Hello!") != 0)
    	 {
    		 close(newsockfd);
    		 printf("Wrong hello!\n");
    		 break;
    	 }
    	 */

    	 {
    		 pthread_mutex_lock( &mutex );
    		 connections.push_front(newsockfd);
    		 pthread_mutex_unlock( &mutex );
    	 }

    	 /*


    	 strcpy(buf, "Get off!");
    	 send(newsockfd, buf, data_size, 0);

    	 close(newsockfd);
    	 printf("Close connection\n");
    	 */
     }

     close(newsockfd);

     close(sockfd);
     return 0;
}
